/**   
 * Copyright 2011 COMERE Harold
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package etrl.core;

import java.util.ArrayList;

import etrl.core.TextureLayer.InvalidTextureFilePathException;
import etrl.shaders.DynamicShaderProgram;

/**
 * \class Node
 * \brief Base class of scene graph nodes.
 * @author harold
 *
 */
public abstract class Node
{
	/**
	 * Name of the node
	 */
	private String _name;
	
	/**
	 * Node callback of the node.
	 */
	private NodeCallback _nodeCallback;
	
	/**
	 * Node's bounding sphere computed from childs bounding spheres
	 */
	private BoundingSphere _boundingSphere;
	
	/**
	 * Flag indicating if the bounding sphere needs to be recomputed or not
	 */
	private boolean _dummyBoundingSphere;
	
	/**
	 * Shader program linked to this node.
	 * If a child has its own shader program, it will be used instead of the father's one
	 */
	private DynamicShaderProgram _program;
	
	/**
	 * Textures layers linked to the node
	 */
	private ArrayList<TextureLayer> _textures;
	
	/**
	 * \brief Initializes an empty node.
	 */
	protected Node()
	{
		_program = null;
		_nodeCallback = null;
		_name = "";
		_textures = new ArrayList<TextureLayer>();
		
		_boundingSphere = new BoundingSphere();
		_dummyBoundingSphere = true;
	}
	
	/**
	 * \brief Sets the name of the node.
	 * @param[in] name Name to set.
	 */
	public void setName(String name)
	{
		_name = name;
	}
	
	/**
	 * \brief Gets the name of the node.
	 * @return Node name.
	 */
	public String getName()
	{
		return _name;
	}
	
	/**
	 * \brief Sets the shader program used by this node.
	 * @param program Shader program to set.
	 */
	public void setShaderProgram(DynamicShaderProgram program)
	{
		_program = program;
	}
	
	/**
	 * \brief Gets the shader program used by this node.
	 * @return Node shader program.
	 */
	public DynamicShaderProgram getShaderProgram()
	{
		return _program;
	}
	
	/**
	 * \brief Adds a new texture layer to the node
	 * @param[in] textureFilePath filePath of the texture image to add.
	 */
	public void addTexture(String textureFilePath)
	{
		addTexture(textureFilePath, 1.f, 1.f);
	}
	
	/**
	 * \brief Adds a new texture layer to the node
	 * @param[in] textureFilePath filePath of the texture image to add.
	 * @param[in] scaleOnU scale applied on texture coordinates on u axis
	 * @param[in] scaleOnV scale applied on texture coordinates on v axis
	 */
	public void addTexture(String textureFilePath, float scaleOnU, float scaleOnV)
	{
		// Try to create the texture layer from the given texture file path
		try
		{
			TextureLayer layer = new TextureLayer(textureFilePath);
			layer.setScale(scaleOnU, scaleOnV);
			_textures.add(layer);
		}
		catch (InvalidTextureFilePathException e)
		{
			// If layer creation fails, it wont be added to node texture
		}
	}	
	
	/**
	 * \brief Gets the current number of texture layers of this node.
	 * @return Number of texture layers applied to the node.
	 */
	public int getTextureCount()
	{
		return _textures.size();
	}
	
	/**
	 * \brief Gets the texture layers of the node.
	 * @return Texture layers list.
	 */
	public ArrayList <TextureLayer> getTextures()
	{
		return _textures;
	}
	
	/**
	 * \brief Defines the node callback to use with this node.
	 * @param[in] nodeCallback Node callback to set.
	 */
	public void setNodeCallback(NodeCallback nodeCallback)
	{
		_nodeCallback = nodeCallback;
	}
	
	/**
	 * \brief Gets the node callback of this node.
	 * @return Node callback.
	 */
	public NodeCallback getNodeCallback()
	{
		return _nodeCallback;
	}
	
	/**
	 * \brief Computes the bounds of this node.
	 * @return Bounding sphere including the node.
	 */
	public BoundingSphere getBounds()
	{
		// If the bounding sphere is invalid, recompute it before to return it
		if(_dummyBoundingSphere) 
		{
			_boundingSphere = computeBoundingSphere();
			_dummyBoundingSphere = false;
		}
		
		// Then return it ...
		return _boundingSphere.clone();
	}
	
	/**
	 * \brief Lets the given node visitor visit the node
	 * @param[in] nodeVisitor node visitor that wants to visit the node
	 */
	public abstract void accept(NodeVisitor nodeVisitor);
	
	/**
	 * \brief Computes the node bouding sphere
	 * @return node bounding sphere
	 */
	public abstract BoundingSphere computeBoundingSphere();
}
